/*
 *  Plan.cpp
 *  Ray Tracing
 *
 *  Created by Jean-Marc Comby + Romain Dequesne on Mon Tue 13 2004.
 *  Copyright (c) 2004. All rights reserved.
 *
 *
 *  Modelisation des plans
 */

#include "Plan.hpp"

#include "Algebre/ConstruitMatrice.hpp"

#include "Utils/StatusCode.hpp"
#include "Utils/debugstreams.hpp"

#include <cstdlib>
#include <cmath>
#include <limits>

using namespace std;

/**
 * Constructeur par défaut. Le plan passe par l'origine et est
 * perpendiculaire a l'axe x.
 */
Plan::Plan()
  : Forme( "Plan" ), VecteurNormal(), Point()
{
  PseudoCentre = Point;
  PseudoRayon = numeric_limits< double >::max();
}

/**
 * Constructeur initialisant les champs du Plan à partir d'un point et
 * un vecteur normal.
 *
 * @param v vecteur normal au Plan.
 * @param a point du Plan.
 */
Plan::Plan(const Vecteur& v, const Coord& a) //attention defini par sa normale //fonctionne
  : Forme( "Plan" ), VecteurNormal( v ), Point(a)
{
  PseudoCentre = Point;
  PseudoRayon = numeric_limits< double >::max();

  MatriceSO3 test;

  StatusCode sc( axeZ( VecteurNormal, test ) );

  if ( ! sc )
    {
      error() << sc.message() << endreq;
      exit( 1 );
    }
  else
    {
      VersLocal  = Transformation( test.inverse(), -Point, 1., false );
      VersGlobal = VersLocal.inverse();
    }
}

/**
 * Constructeur initialisant les champs du Plan à partir de trois
 * points du plan.
 *
 * @param a point du Plan.
 * @param b point du Plan.
 * @param c point du Plan.
 */
Plan::Plan(const Coord& a, const Coord& b, const Coord& c) //attention defini par sa normale //fonctionne
  : Forme( "Plan" ), VecteurNormal( Vecteur( a, b ) ^ Vecteur( a, c ) ),
    Point(a)
{
  PseudoCentre = Point;
}

/**
 * Constructeur de copie, permet de dupliquer un Plan.
 *
 * @param plan objet à copier.
 */
Plan::Plan(const Plan  &plan)
  : Forme( "Plan" ), VecteurNormal( plan.vecteurnormal() ),
    Point( plan.point() )
{
  PseudoCentre = plan.point();
}

/**
 * Le destructeur ne fait rien de spécial.
 */
Plan::~Plan()
{
}

/**
 * Méthode qui permet de savoir si le rayon lancé a une intersection
 * avec le plan.
 *
 * Comme il est impossible de définir une sphère d'exclusion, on
 * travaille avec la normale : si le plan est parallèle au rayon, on
 * teste si le point d'origine est dans le plan. Si le rayon n'est pas
 * parallèle au plan ou si le plan contient l'origine du rayon, alors
 * il y a intersection.
 *
 * @param[in] rayon instance de Rayon lancé, pur laquelle on veut
 * savoir si une intersection existe.
 *
 * @retval true si le rayon passe à une distance inférieure à
 * PseudoRayon, @retval false sinon.
 */
bool Plan::aIntersection( const Rayon& rayon ) const
{
  if ( abs( VecteurNormal * rayon.direction() ) > 1.e-5 )
    return true;
  else if ( estSur( rayon.origine() ) )
    return true;
  else
    return false;
}

/**
 * Permet de récupérer le vecteur normal au Plan.
 *
 * @return une référence constante sur VecteurNormal
 */
const VecteurU& Plan::vecteurnormal() const
{
  return VecteurNormal;
}

/**
 * Permet de récupérer le point du Plan.
 *
 * @return une référence constante sur Point.
 */
const Coord& Plan::point() const
{
  return Point;
}

/**
 *
 */
Coord Plan::trouveIntersection(Rayon const & ray) const //on ne traite plus que le cas ou il y a intersection
{
  double nr(VecteurNormal*ray.direction());
  Vecteur QP(ray.origine(),Point); //vecteur QP par constructeur special
  double Lambda((QP*VecteurNormal)/nr);
  if ( abs( nr ) < 1.e-7 || Lambda < -1.e-7 )
    return NullePart;

  Coord inter( ray.origine() );
  inter += Lambda * ray.direction(); //formule pour l'intersection
  return inter;
}

/**
 * Renvoie simplement VecteurNormal.
 */
VecteurU Plan::normale(const Coord&a) const //fonctionne
{
  return VecteurNormal; //renvoi la normale
}

bool Plan::estDans(const Coord& c) const //fonctionne
{
  Vecteur a(c);
  a -= Vecteur( Point );
  if (  a * VecteurNormal < -1.e-7 ) return true;
  else return false;
}

bool Plan::estSur(const Coord& c) const //fonctionne
{
  Vecteur a(c);
  a -= Vecteur( Point );
  if ( abs( a * VecteurNormal ) < 1e-7 ) return true;
  else return false;
}

void Plan::mapping(const Coord &intersection, int &i, int &j,
		   const int &frequence, const int &Hauteur,
		   const int &Largeur) const
{
  Coord carte( intrinseque( intersection ) );

  if ( carte == NullePart )
    {
      i = j = 0;
      return;
    }

  double t;

  t = double( frequence ) * carte.x();
  i =  static_cast< int >( floor( t ) );

  t = double( frequence ) * carte.y();
  j =  static_cast< int >( floor( t ) );

  i = i % Largeur;
  j = j % Hauteur;
  /*
  //calcul du pixel(i,j) correspondant à (y,z)
  t = ((int) ((intersection.y()-(double)frequence/2.0) /(double)frequence)) * frequence;
  j = -1*(int) (Largeur * ((intersection.y()-frequence/2) - t) / frequence);
  if (j<0) j=Largeur+j;
  
  
  m = (double) frequence *(double) Hauteur / (double) Largeur;
  t = ((int) ((intersection.z()+m/2) / m)) * m;
  i = (int) (Hauteur * ((intersection.z()+m/2) - t) / m);
  if (i<0) i=Hauteur+i;
  */
}

/**
 * Affiche Point et VecteurNormal.
 */
ostream& Plan::affiche(ostream& os) const
{
  os << "Forme : " << nom() << endl << "Paramètres :" << endl
     << "\tPoint       : " << Point << endl
     << "\tOrientation : " << VecteurNormal << endl;

  return os;
}

Coord Plan::intrinseque(const Coord& pt) const
{
  if ( ! estSur( pt ) )
    return NullePart;
  
  return VersLocal( pt );
}
